package fields;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Scanner;

import data.DataMatcher;

import field_REGEX_Definitions.*;
import field_REGEX_Definitions.DebixFields.*;
import field_REGEX_Definitions.IFAfields.*;
import field_REGEX_Definitions.Other.*;
import fileProcessing.ParsingException;
/**
 * Designed to parse lines to determine what information is present depending on the field matched.
 * @author ashepard
 *
 */
public class FieldMatcher {
	private static Field [] fields = {new Generational_Suffix(),new Middle_Name(),new Zip_Code(),
			new Address_1(),new Address_2(),new Bank_Account_1(),new Bank_Account_2(),new Bank_Account_3(),
			new Card_Number(),new Card_Type(),new City(),new Country(),new CVV(),new Date_of_Issue(),
			new DOB(),new Drivers_License_State(),new Drivers_License(),new Email(),new Expiration_Date(),
			new First_Name(),new Full_Name(),new Last_Name(),new Mothers_Maiden_Name(),new Name_on_Card(),
			new Organization_Name(),new Password(),new Phone(),new PIN(),new Routing_Number(),
			new Security_Question_1(),new Security_Question_2(),new Security_Question_Answer_1(),
			new Security_Question_Answer_2(),new Security_Question_Answer_3(),new Social_Security_Number(),
			new State(),new Username(),new Collection_Date(),new URL_Compromised(),new URL_Source(),new DefaultMatcher()};
	
	private static ArrayList<CustomField> customFields = new ArrayList<CustomField>();
	
	private static String[] headerLineCheck = {"suffix","zip\\s*code","postal\\s*code","address","bank\\s*account",
		"card\\s*number","credit\\s*card","card\\s*type","card","city","coutry","cvv","security\\s*code",
		"date","issued","birth","dob","state","license","email","expir","organization","business",
		"company","password","psswrd","name","phone","pin","personal\\s*i[a-z]*","account\\s*n[a-z]*",
		"routing\\s*n[a-z]*","security\\s*q[a-z]*[1-9]*","ssn","social\\s*s[a-z]*","url","[a-z]*site","host",
		"application","login","user","admin","level","id"};
	private static ArrayList<String> customSearchWords = new ArrayList<String>(1000);
	
	private static DataMatcher dataMatcher = new DataMatcher();

	private FieldMatcher(){};
	
	private static class FM_holder { 
        public static final FieldMatcher instance = new FieldMatcher();
	}

	public static FieldMatcher getInstance() {
        return FM_holder.instance;
	}
	
	/**
	 * Creates a new AbstractField when a line of text matches the field title.
	 * Field will be given the name of match and the category of the 
	 * destination reporting database ("Debix","IFA","Other").
	 * @param line
	 * @return
	 */
	public static AbstractField getField(String line){
		AbstractField result = null;
		for(Field f : fields){
			if(f.matches(line)){
				result = f.execute();
			}
		}
		return result;
	}
	
	public static boolean matchesField(String s){
		for(Field f : fields){
			if(f.matches(s)){
				if(f.getFieldType().equalsIgnoreCase("default"))
					return false;
				else{
					return true;
				}
					
			}
		}
		if(customFields.size() > 0){
			for(CustomField cf : customFields){
				if(cf.matches(s))
					return true;
			}
		}
		return false;
	}
	
	public String checkAsDataType(String s) throws ParsingException{
		return dataMatcher.matchesData(s);
	}
	
	public String[] getHeaderLineCheck(){
		return headerLineCheck;
	}
	
	public ArrayList<String> getCustomHeaderLineCheck(){
		return customSearchWords;
	}
	
	public ArrayList<CustomField> getCustomFields(){
		return customFields;
	}
	
	public void addNewCustomField(String fieldname,String type,String aliases){
		customFields.add(new CustomField(fieldname,type,aliases.split(",")));
	}
	
	public void addNewSearchWord(String searchWord){
		if(!customSearchWords.contains(searchWord.toLowerCase()))
			customSearchWords.add(searchWord.toLowerCase());
	}
	public void removeSearchWord(int index) {
		customSearchWords.remove(index);
	}
	
//	public void printSearchWords() throws IOException{
//		FileWriter newWriter = new FileWriter("searchwords_list.txt");
//		for(String s : customSearchWords)
//			newWriter.write("\""+s+"\""+",");
//		newWriter.close();
//	}
	
	public void newFieldConstructor(){
		Scanner input = new Scanner(System.in);
		String fieldname,type,aliasList;
		System.out.println("Enter the official name for this field.");
		System.out.print("Field Name: ");
		fieldname = input.nextLine();
		System.out.println();
		System.out.println("Please specify which database(s) (if any) this field is reportable to.");
		System.out.println("Examples: \"Debix\", \"IFA\", \"Other\", etc.");
		System.out.println("If field is reportable to multiple databases please separate each with a comma.");
		System.out.print("Reportable to: ");
		type = input.nextLine();
		System.out.println();
		System.out.println("Provide all aliases of this field. These names will be included in subsequent searches.");
		System.out.println("Separate each alias with a comma, (ex. name1,name2,name3,etc.)");
		System.out.print("Aliases: ");
		aliasList = input.nextLine();
		addNewCustomField(fieldname, type, aliasList);
	}
	
	public void addBatchKeywords(File batchFile) throws ParsingException{
		try {
			BufferedReader inFile = new BufferedReader(new FileReader(batchFile));
			String newLine = inFile.readLine().toLowerCase();
			while(newLine != null){
				if(!customSearchWords.contains(newLine)){
					customSearchWords.add(newLine);
				}
			newLine = inFile.readLine();
			}
		} catch (FileNotFoundException e) {
			throw new ParsingException(e.getMessage());
		} catch (IOException e1) {
			throw new ParsingException(e1.getMessage());
		}
	}
	
	public static Integer[] getPossibleLengths(String type){
		//collect the possible number tokens which may be seen depending on the fields to match.
		//place in matchLength;
		if(type.equalsIgnoreCase("name") || type.equalsIgnoreCase("full name")){
			return new Integer[]{1,2,3};
		} else if(type.equalsIgnoreCase("address")){
			return new Integer[]{1,3,4};
		} else if(type.equalsIgnoreCase("city")){
			return new Integer[]{1,2};
		} else if(type.equalsIgnoreCase("state")){
			return new Integer[]{1,2};
		} else if(type.equalsIgnoreCase("zip code")){
			return new Integer[]{1,2};
		} else if(type.equalsIgnoreCase("social security number")){
			return new Integer[]{1,3};
		} else if(type.equalsIgnoreCase("phone number")){
			return new Integer[]{1,2,3,4,};
		} else if(type.equalsIgnoreCase("date of birth")){
			return new Integer[]{1,3};
		} else if(type.equalsIgnoreCase("driver's license number")){
			return new Integer[]{1};
		} else if(type.equalsIgnoreCase("driver's license state")){
			return new Integer[]{1};
		} else if(type.equalsIgnoreCase("card type")){
			return new Integer[]{1,2};
		} else if(type.equalsIgnoreCase("card number")){
			return new Integer[]{1,4};
		} else if(type.equalsIgnoreCase("cvv")){
			return new Integer[]{1};
		} else if(type.equalsIgnoreCase("expiration date")){
			return new Integer[]{1,2};
		} else if(type.equalsIgnoreCase("date of issue")){
			return new Integer[]{1,2,3};
		} else if(type.equalsIgnoreCase("organization name")){
			return new Integer[]{0};
		} else if(type.equalsIgnoreCase("routing number")){
			return new Integer[]{1};
		} else if(type.equalsIgnoreCase("bank account number")){
			return new Integer[]{1};
		} else if(type.equalsIgnoreCase("username")){
			return new Integer[]{1};
		} else if(type.equalsIgnoreCase("name on card")){
			return new Integer[]{1,2,3};
		} else if(type.equalsIgnoreCase("first name") || 
				type.equalsIgnoreCase("middle name") ||
				type.equalsIgnoreCase("last name")){
			return new Integer[]{1};
		} else if(type.equalsIgnoreCase("password")){
			return new Integer[]{1};
		} else if(type.equalsIgnoreCase("country")){
			return new Integer[]{1,2};
		} else if(type.equalsIgnoreCase("email")){
			return new Integer[]{1};
		} else if(type.equalsIgnoreCase("pin")){
			return new Integer[]{1};
		} else if(type.equalsIgnoreCase("mother's maiden name")){
			return new Integer[]{1};
		} else if(type.equalsIgnoreCase("security question")){
			return new Integer[]{0};
		} else if(type.equalsIgnoreCase("security question answer")){
			return new Integer[]{0};
		} else{
			return new Integer[]{1};
		}
	}
}
